threst's Blog

逆向入门--linux64

2018/05/12 Share

序幕

这个Ground Zero系列适合初学者入门使用,让他们进入逆向工程领域。因为这是x64的时代,所以我跳过了x86体系结构。但是你要知道,所有将用c++编写的示例也可以编译为x86,但是我将把它作为作业留给你们。如果你没有任何经验,也无所谓了。你唯一需要的就是有对编程语言的基本理解。
开始时,我们将编写一个简单的c++程序,它将提示用户输入密码。它将检查密码是否匹配,如果它正确,它将提示正确,否则将提示错误。我举这个例子的主要原因是,这个例子将让你了解jump,以及其他类似的条件在汇编语言中是如何工作的。另一个原因是,大多数具有硬编码键的程序都可以用类似的方式破解,只不过需要更多的数学知识,而这正是大多数盗版分销商如何破解合法软件并传播密钥的方式。
让我们先了解一下我们编写的c++程序。所有的代码都将托管在我的Github页面中:
https://github.com/paranoidninja/ScriptDotSh-Reverse-Engineering

这里的代码非常简单。我们的程序将一个参数作为密码输入,如果我不输入任何密码,它将打印help命令。如果我指定了一个密码,它会以10个字节的字符形式存储,并将密码发送到check_pass()函数。我们的硬编码密码是check_pass()函数中的PASSWORD1。在这里,我们的密码与strcmp90函数的实际密码变量mypass进行比较。如果密码匹配,则返回零,否则返回1。返回到我们的主功能,如果我们接收到1,它会打印错误的密码,否则会打印正确的密码。

现在,让我们在GDB调试器中找到这个代码。用GDB执行二进制文件,将首先在main中设置一个断点,然后再发送参数。其次,我们会让enable time在我们的GDB上运行,所以如果我们走错了一步,我们可以逆转这一步。这可以通过以下命令完成:target record-fullreverse-stepi / nexti

如果你不明白这一点,问题不大。你只需关注gdb $部分,就像你上面看到的那样,在给断点使用break main之后,我输了一个错误的密码pass123。我的编译过的代码应该如前所述打印一个不正确的密码,但是随着我们继续,我们会找到两种方法来绕过代码; 一种是通过从内存中取出实际密码,二是通过修改跳转值并打印密码正确。

反汇编

下一步是反汇编整个代码,看看到底发生了什么:

我们在整个反汇编代码中的主要关注以下几点:

1.je - je表示如果相等就跳转到某个地址,如果不相等,继续下一步。
2.call-调用一个新的函数。请记住,在加载完成后,反汇编代码将从主反汇编函数更改为新函数的反汇编代码。

  1. test - 检查两个值是否相等
  2. cmp- 比较两个值
  3. JNE - 如果它不等于某物,jne就意味着跳转。否则,继续下一步。

有些人可能会问,既然我们有cmp,我们为什么要进行test呢?答案可以在这里找到,解释得很漂亮:
https://stackoverflow.com/questions/39556649/linux-assembly-whats-difference-between-test-eax-eax-and-cmp-eax-0

所以,如果我们看到上面的反汇编代码,我们知道如果我们运行没有密码或参数的二进制文件,它将打印帮助,否则将继续检查密码。所以这个cmp应该是检查我们是否有参数的部分。如果一个参数不存在,它将继续打印帮助,否则它将跳转到<main + 70>。如果你在左边的地址旁边看到那些数字,我们可以看到在<+70>处,我们正在将某些东西移动到rax寄存器中。所以,我们要做的是我们将在je上设置一个断点,方法是指定它的地址0x0000000000400972,然后看看它是跳转到<+70>通过要求它继续c。GDB命令c将继续运行二进制文件,直到遇到另一个断点。

而现在,如果您执行的是step迭代的stepi,它将执行一次迭代执行,并且它应该将您带到<+70>它将Quad Word移入rax寄存器的位置。

我们的逻辑到现在为止都是正确的,现在我们来看下一个有趣的东西,也就是调用部分。如果你看到它旁边,它上面写着类似于<_Z10check_passPc>,它就是我们的check_pass()函数。让我们跳到使用stepi,看看函数里面是什么。一旦你跳到check_pass()函数并反汇编它,你会看到一组新的反汇编代码,它就是check_pass()函数本身的代码。这里有四行有趣的代码:

第一部分是将rdx寄存器的值移至rsi并将rax移至rdi。下一部分是调用strcmp()函数,它是C ++的字符串比较函数。接下来,我们有测试这两个值进行比较,如果这两个值相等,我们跳(JE)到<_Z10check_passPc + 77>将0移动到EAX寄存器。如果值不相等,函数将继续在<+70>处继续并在eax寄存器赋值1。现在,这些只不过是我们先前在check_pass()函数中指定的返回值。由于我们输入了无效密码,将发送的返回值为1。但是如果我们可以将返回值修改为零,那么它将输出为“正确的密码”。

另外,我们可以继续检查被移动到rsirdi寄存器中的内容。所以,让我们在那里放置一个断点并直接跳到它。

从上图可以看出,我使用了x / s $ rdxx / s $ rax命令从寄存器中获取值。x / s表示检查寄存器并将其显示为一个字符串。如果你想以字节为单位获得它,你可以指定x / b,或者如果你想要字符,你可以指定x / c等等。然而,有多种变化。现在我们获取密码的第一部分已经在这里了。但是,让我们看看我们如何将<_Z10check_passPc + 70>处的返回值修改为零。所以,我们会拍摄stepi并跳到这个迭代。

结语

正如您在上面看到的,函数在二进制中将0x1移到eax,但是在它可以做一个je之前,我们使用set $eax = 0x0将值修改为0x0,然后继续使用c作为下面的函数,瞧!!!我们有一个返回的值作为正确的密码!


这只是一个简单的例子,让您开始逆向工程。随着我们深入,我们将看到套接字函数,运行时加密,编码隐藏的域名等等。这整个过程可以在Windows中使用Olly调试器完成,我将在下一篇博文中展示。

原文地址:https://scriptdotsh.com/index.php/2018/04/09/ground-zero-part-1-reverse-engineering-basics/

CATALOG
  1. 1. 序幕
  2. 2. 反汇编
  3. 3. 结语